package cn.monkey.family.server.domain.uc.auth;

import cn.monkey.family.data.Result;
import cn.monkey.family.data.Results;
import cn.monkey.family.data.context.RequestContext;
import cn.monkey.family.data.uc.auth.Authorization;
import cn.monkey.family.data.uc.auth.UserSession;
import cn.monkey.family.data.uc.org.Organization;
import cn.monkey.family.data.uc.org.role.OrgRole;
import cn.monkey.family.data.uc.role.RoleVo;
import cn.monkey.family.data.uc.user.User;
import cn.monkey.family.data.uc.user.role.UserRoleMapper;
import cn.monkey.family.data.uc.user.role.UserRoleVo;
import cn.monkey.family.server.domain.uc.auth.session.UserSessionRepository;
import cn.monkey.family.server.domain.uc.org.role.OrgRoleRepository;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

public class AfterAuthorizeHandler implements IAfterAuthorizeHandler {

    final AuthorizationManager authorizationManager;
    final UserRoleMapper userRoleMapper;
    final OrgRoleRepository orgRoleRepository;
    final UserSessionRepository userSessionRepository;

    public AfterAuthorizeHandler(AuthorizationManager authorizationManager,
                                 UserRoleMapper userRoleMapper,
                                 OrgRoleRepository orgRoleRepository,
                                 UserSessionRepository userSessionRepository) {
        this.authorizationManager = authorizationManager;
        this.userRoleMapper = userRoleMapper;
        this.orgRoleRepository = orgRoleRepository;
        this.userSessionRepository = userSessionRepository;
    }


    public Result<Authorization> handle(RequestContext requestContext, User user) {
        return this.afterAuthorize(requestContext, user);
    }

    private Result<Authorization> afterAuthorize(RequestContext requestContext, User user) {
        this.trySaveUserSession(requestContext, user);
        return this.buildAuthorization(requestContext, user);
    }

    private Result<Authorization> buildAuthorization(RequestContext requestContext, User user) {
        Authorization authorization = new Authorization();
        authorization.setUid(user.getId());
        authorization.setToken(this.authorizationManager.encrypt(authorization));
        return Results.ok(authorization);
    }


    private void trySaveUserSession(RequestContext requestContext, User user) {
        UserSession userSession = new UserSession();
        userSession.setUid(user.getId());
        Map<String, Collection<RoleVo>> orgRoles = this.findUserOrgRoles(user);
        userSession.setOrgRoles(orgRoles);
        this.userSessionRepository.save(userSession);
    }

    private Map<String, Collection<RoleVo>> findUserOrgRoles(User user) {
        UserRoleVo userRoleVo = this.userRoleMapper.toVo(user);
        Collection<RoleVo> roleVos = userRoleVo.getRoles();
        if (CollectionUtils.isEmpty(roleVos)) {
            return Collections.emptyMap();
        }
        Set<String> roleIds = roleVos.stream().map(RoleVo::getId).collect(Collectors.toSet());
        Collection<OrgRole> orgRoles = this.orgRoleRepository.findAllByRoleIdIn(roleIds);
        Map<String, Collection<RoleVo>> orgRoleMap = new HashMap<>();
        Map<String, String> roleOrgIdMap = orgRoles.stream().collect(Collectors.toMap(OrgRole::getRoleId, OrgRole::getOrgId));
        for (RoleVo roleVo : roleVos) {
            String orgId = roleOrgIdMap.get(roleVo.getId());
            String key = Optional.ofNullable(orgId).orElse(Organization.EMPTY_ID);
            orgRoleMap.compute(key, (k, v) -> {
                if (v == null) {
                    v = new ArrayList<>();
                }
                v.add(roleVo);
                return v;
            });
        }
        return orgRoleMap;
    }
}
